package com.shigen.demo.interceptor;

import cn.hutool.core.util.StrUtil;
import com.shigen.demo.Enum.HttpStatusCode;
import com.shigen.demo.annotation.RequestLimit;
import com.shigen.demo.bean.Result;
import com.shigen.demo.util.Common.Redis;
import com.shigen.demo.util.RedisUtil;
import com.shigen.demo.util.ResponseUtil;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Slf4j
@Component
public class RequestLimitInterceptor extends HandlerInterceptorAdapter {

  @Autowired
  private RedisUtil redisUtil;

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    /**
     * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口
     * isAssignableFrom()方法是判断是否为某个类的父类
     * instanceof关键字是判断是否某个类的子类
     */
    if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
      //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
      HandlerMethod handlerMethod = (HandlerMethod) handler;
      Method method = handlerMethod.getMethod();
      // 获取方法中是否包含注解
      RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
      //获取 类中是否包含注解，也就是controller 是否有注解
      RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
      // 如果 方法上有注解就优先选择方法上的参数，否则类上的参数
      RequestLimit requestLimit = methodAnnotation != null ? methodAnnotation : classAnnotation;
      if (requestLimit != null) {
        if (isLimit(request, requestLimit)) {
          ResponseUtil.responseOut(response, new Result(HttpStatusCode.TOO_MANY_REQUESTS));
          return false;
        }
      }
    }
    return super.preHandle(request, response, handler);
  }

  //判断请求是否受限
  public boolean isLimit(HttpServletRequest request, RequestLimit requestLimit) {
    String token = request.getHeader("token");
    if (StrUtil.isBlank(token)) {
      return false;
    }
    String limitKey = Redis.REQ_LIMIT_PREFIX + request.getServletPath() + "-" + token;
    // 从缓存中获取，当前这个请求访问了几次
    String redisCount = redisUtil.get(limitKey);
    if (redisCount == null) {
      //初始 次数
      redisUtil.setEx(limitKey, String.valueOf(1), requestLimit.second(), TimeUnit.SECONDS);
    } else {
      if (Integer.parseInt(redisCount) >= requestLimit.maxCount()) {
        log.error(limitKey);
        return true;
      }
      // 次数自增
      redisUtil.incrBy(limitKey, 1);
    }
    return false;
  }
}
